package websocket

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	"net/http"
	"sync"
)

type GroupConn struct {
	message             chan *broadcastMessage
	groups              map[*GConn]bool
	keys                map[string]bool
	keyGeneratorHandler func(key interface{}) (string, error)
	status              int // 生命周期
	registerConn        chan *GConn
	mutex               sync.Mutex
}

type broadcastMessage struct {
	messageType int
	message     []byte
}

type GConn struct {
	Conn
	key string
}

const (
	create  = 1
	running = 2
	died    = 3
)

func NewGroupConn() *GroupConn {
	return &GroupConn{
		message:      make(chan *broadcastMessage, 1),
		groups:       make(map[*GConn]bool, 0),
		keys:         make(map[string]bool, 0),
		status:       create,
		registerConn: make(chan *GConn, 1),
	}
}

func (group *GroupConn) Release() {

}

func (group *GroupConn) NewConn(seed interface{}, w http.ResponseWriter, r *http.Request, responseHeader http.Header) (Conn, error) {
	var gConn *GConn = nil
	var err error = nil

	if group.status == died {
		err = errors.New("current group is died therefore denied to new connection")
	}

	// 并发
	group.mutex.Lock()
	key, err := group.getKeyGeneratorHandler()(seed)
	if err != nil {
		group.mutex.Unlock()
		return nil,err
	}

	if _, ok := group.keys[key]; ok {
		group.mutex.Unlock()
		// 若存在，表示已加入群组，不能再加
		return nil,errors.New("current user is one of this group")
	}

	conn, err := NewConnection(w, r, responseHeader)
	if err != nil {
		group.mutex.Unlock()
		return nil,err
	}

	gConn = &GConn{
		Conn: conn,
		key:  key,
	}

	gConn.JoinGroup(group)
	group.registerConn <- gConn

	return gConn, nil
}

func (group *GroupConn) getKeyGeneratorHandler() func(seed interface{}) (string, error) {
	if group.keyGeneratorHandler != nil {
		return group.keyGeneratorHandler
	}

	return keyGenerator
}

func keyGenerator(seed interface{}) (string, error) {
	if seed == nil {
		return "", errors.New("param::seed is nullptr")
	}

	md5buffer := md5.New()
	if size, err := fmt.Fprint(md5buffer, seed); size == 0 || err != nil {
		return "", errors.New(fmt.Sprintf("seed is empty or %s", err.Error()))
	}

	return hex.EncodeToString(md5buffer.Sum(nil)), nil
}

func (group *GroupConn) Broadcast() {
	for true {
		select {
		case conn := <-group.registerConn:
			group.keys[conn.key] = true
			group.groups[conn] = true
			if group.status == create {
				group.status = running
			}
			group.mutex.Unlock()
		case message, ok := <-group.message:
			if !ok {
				break
			}
			for conn := range group.groups {
				select {
				case <- conn.Conn.(*Connection).isMessageChanelClose:
					delete(group.keys, conn.key)
					delete(group.groups, conn)
				default:
					conn.SendMessage(message.messageType, message.message)
				}
			}
			if len(group.groups) == 0 {
				close(group.message)
				group.status = died
			}

		default:
			if len(group.groups) == 0 && group.status == died {
				break
			}
		}
	}
}

func (group *GroupConn) ReceiveMessage(messageType int, message []byte) {
	group.message <- newBroadCastMessage(messageType, message)
}

func (group *GroupConn) Run() {
	group.Broadcast()
}

func newBroadCastMessage(messageType int, message []byte) *broadcastMessage {
	return &broadcastMessage{
		messageType: messageType,
		message:     message,
	}
}
